package com.mytx.search.scheduled;

import com.alibaba.fastjson.JSONObject;
import com.mytx.common.constant.Constant;
import com.mytx.common.pojo.RedisLock;
import com.mytx.common.utils.DateUtils;
import com.mytx.common.utils.EntityUtils;
import com.mytx.mapper.*;
import com.mytx.pojo.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.*;

@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Component
public class ScheduledTask {
    Logger logger = LoggerFactory.getLogger(ScheduledTask.class);
    //每次从redis中返回的数据，具体一次返回多少才科学，不是很清楚，暂且返回1000
    private static final long QUERY_COUNT = 100;
    @Autowired
    TopicMapper topicMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    UserMapper userMapper;


    @Autowired
    OrdersMapper ordersMapper;


    @Autowired
    TaskMapper taskMapper;

    /**
     * 悬赏任务在确认后，雇主发起取消任务，则进入6天后自动取消的状态
     */
    @Scheduled(cron = "0 59 0-23 * * ? ")//0-24小时，每个小时的59分钟开始执行
   // @Scheduled(cron = "0 0/1 * * * ?")//每1分钟执行一次
    public void rewardTaskCancelScheduledTask() {

         String key = DateUtils.formatDate(new Date(), DateUtils.DATE_FORMAT_DATE_HOUR);
      //  String key = DateUtils.formatDate(new Date(), "yyyy-MM-dd-HH-mm");

        Long size = redisTemplate.opsForSet().size(Constant.TASK_CANCEL_SET + key);
        if (size == null || size == 0) {
            logger.info("rewardTaskCancelScheduledTask:没有悬赏任务需要自动取消 key:{}", key);
            return;
        }
        boolean haveMoreData = true;
        while (haveMoreData) {
            long count;
            if (size > QUERY_COUNT) {
                size = size - QUERY_COUNT;
                count = QUERY_COUNT;
            } else {
                count = size;
                haveMoreData = false;
            }

            List<String> taskIdList = redisTemplate.opsForSet().pop(Constant.TASK_CANCEL_SET + key, count);

            if (taskIdList == null || taskIdList.size() == 0) {
                logger.error("rewardTaskCancelScheduledTask:到期任务列表id为空");
                continue;
            }
            List<String> taskKeyList = new ArrayList<>();
            for (int i = taskIdList.size() - 1; i >= 0; i--) {
                String taskId = taskIdList.get(i);
                String key1 = Constant.TASK_RECORD + taskId;
                taskKeyList.add(key1);
            }
            Date updated = new Date();
            //批量缓存清除
            List list = redisTemplate.opsForValue().multiGet(taskKeyList);
            for (Object o : list) {
                Task task = (Task) o;
                task.setStatus(Constant.TASK_STATUS_EXPIRY_AUTO_CANCEL);
                task.setUpdated(updated);
                JSONObject info = new JSONObject();
                List<String> userIdList = new ArrayList<>();
                userIdList.add(task.getuId());
                userIdList.add(task.getEmployerId());
                info.put("userIdList", userIdList);
                info.put("task", task);
                redisTemplate.convertAndSend(Constant.NOTIFY_TASK_INFO, info);
            }
            //清除缓存
            redisTemplate.delete(taskKeyList);
            //批量横线进mysql 只更新状态和 更新时间
            TaskExample example = new TaskExample();
            TaskExample.Criteria criteria = example.createCriteria();
            criteria.andIdIn(taskIdList);
            Task updateTask = new Task();
            updateTask.setStatus(Constant.TASK_STATUS_EXPIRY_AUTO_CANCEL);
            updateTask.setUpdated(updated);
            taskMapper.updateByExampleSelective(updateTask, example);

        }
    }

    /**
     * 悬赏任务在确认后，雇主发起取消任务，则进入6天后自动结算状态
     */
     @Scheduled(cron = "0 59 0-23 * * ? ")//0-24小时，每个小时的59分钟开始执行
    //@Scheduled(cron = "30 0/1 * * * ?")//每1分钟执行一次结算
    public void rewardTaskFinishScheduledTask() {

         String key = DateUtils.formatDate(new Date(), DateUtils.DATE_FORMAT_DATE_HOUR);
       // String key = DateUtils.formatDate(new Date(), "yyyy-MM-dd-HH-mm");

        Long size = redisTemplate.opsForSet().size(Constant.TASK_FINISH_SET + key);
        if (size == null || size == 0) {
            logger.info("rewardTaskCancelScheduledTask:没有悬赏任务需要自动解锁 key:{}", key);
            return;
        }
        boolean haveMoreData = true;
        while (haveMoreData) {
            long count;
            if (size > QUERY_COUNT) {
                size = size - QUERY_COUNT;
                count = QUERY_COUNT;
            } else {
                count = size;
                haveMoreData = false;
            }

            List<String> taskIdList = redisTemplate.opsForSet().pop(Constant.TASK_FINISH_SET + key, count);

            if (taskIdList == null || taskIdList.size() == 0) {
                logger.error("rewardTaskCancelScheduledTask:到期任务列表id为空");
                continue;
            }
            List<String> taskKeyList = new ArrayList<>();
            for (int i = taskIdList.size() - 1; i >= 0; i--) {
                String taskId = taskIdList.get(i);
                String key1 = Constant.TASK_RECORD + taskId;
                taskKeyList.add(key1);
            }
            Date updated = new Date();
            //批量缓存清除
            List list = redisTemplate.opsForValue().multiGet(taskKeyList);
            for (Object o : list) {
                Task task = (Task) o;
                String taskId = task.getId();
                RedisLock lock = new RedisLock(redisTemplate, Constant.TASK_LOCK + taskId);
                try {
                    if (!lock.lock()) {
                        logger.error("rewardTaskFinishScheduledTask:没有获取到锁，taskId:{}", taskId);
                        break;
                    }
                    task.setStatus(Constant.TASK_STATUS_EXPIRY_AUTO_FINISH);
                    task.setUpdated(updated);
                    BigDecimal amount = task.getAmount();
                    String employeeId = task.getuId();
                    String employerId = task.getEmployerId();

                    Topic topic = topicMapper.selectByPrimaryKey(task.gettId());
                    if (topic.getStatus() == Constant.TOPIC_STATUS_REWARD_COMPLETE) {
                        logger.error("rewardTaskFinishScheduledTask:该任务对应的主题已经结算，taskId:{},topicId:{}", taskId, task.gettId());

                        task.setStatus(Constant.TASK_STATUS_OTHER_ONE_COMPLETE);
                        task.setUpdated(new Date());
                        taskMapper.updateByPrimaryKeySelective(task);
                        List<String> userIdList = new ArrayList<>();
                        userIdList.add(task.getuId());
                        JSONObject info = new JSONObject();
                        info.put("userIdList", userIdList);
                        info.put("task", task);
                        redisTemplate.convertAndSend(Constant.NOTIFY_TASK_INFO, info);

                        break;
                    }
                    topic.setStatus(Constant.TOPIC_STATUS_REWARD_COMPLETE);
                    topic.setUpdated(updated);
                    topicMapper.updateByPrimaryKeySelective(topic);

                    Task updateTask = new Task();
                    updateTask.setId(taskId);
                    updateTask.setStatus(Constant.TASK_STATUS_EXPIRY_AUTO_FINISH);
                    updateTask.setUpdated(updated);
                    taskMapper.updateByPrimaryKeySelective(updateTask);

                    BigDecimal targetUserMysqlBalance = userMapper.incrementBalance(employeeId, amount);
                    User user = userMapper.payV2(employerId, amount, amount);
                    redisTemplate.opsForHash().put(Constant.USER_SESSION + employeeId, Constant.BALANCE, targetUserMysqlBalance);
                    redisTemplate.opsForHash().putAll(Constant.USER_SESSION + employerId, EntityUtils.objectToMap_v1(user));
                    logger.info("rewardTaskFinishScheduledTask:悬赏任务自动结算，雇佣者余额增加 ，uId:{},amount:{},balance:{}", employeeId, amount, targetUserMysqlBalance);
                    logger.info("rewardTaskFinishScheduledTask:悬赏任务自动结算，雇主余额减少 ，uId:{},amount:{},balance:{}", employerId, amount, user.getBalance());
                    logger.info("rewardTaskFinishScheduledTask:悬赏任务自动结算，雇主保证金减少 ，uId:{},amount:{},deposit:{}", employerId, amount, user.getDeposit());
                    JSONObject info = new JSONObject();
                    List<String> userIdList = new ArrayList<>();
                    userIdList.add(task.getuId());
                    userIdList.add(task.getEmployerId());
                    info.put("userIdList", userIdList);
                    info.put("task", task);
                    redisTemplate.convertAndSend(Constant.NOTIFY_TASK_INFO, info);
                    redisTemplate.delete(Constant.TASK_RECORD + taskId);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }

            }
        }
    }


    /**
     * 每天1分钟执行一次结算
     * 根据当前时间生成key,去redis 读取是否有需要结算的拍卖
     */
    //0/5 * *  * * ?
    //  @Scheduled(cron = "0 0/1 * * * ?")//每1分钟执行一次结算   第一版不上线拍卖功能，暂且注释掉
    public void topicAuctionScheduledTask() {

        String key = DateUtils.formatDate(new Date(), "yyyy-MM-dd-HH-mm");
        logger.info("topicAuctionScheduledTask:进入定时任务 key:{}", key);
        Long size = redisTemplate.opsForSet().size(Constant.AUCTION_HANDLE_RECORD + key);
        if (size == null || size == 0) {
            logger.info("topicAuctionScheduledTask:没有拍卖商品需要处理 key:{}", key);
            return;
        }
        boolean haveMoreData = true;
        while (haveMoreData) {
            long count;
            if (size > QUERY_COUNT) {
                size = size - QUERY_COUNT;
                count = QUERY_COUNT;
            } else {
                count = size;
                haveMoreData = false;
            }
            List<String> topicIdList = redisTemplate.opsForSet().pop(Constant.AUCTION_HANDLE_RECORD + key, count);
            if (topicIdList == null || topicIdList.size() == 0) {
                logger.error("topicAuctionScheduledTask:拍卖商品列表id为空");
                continue;
            }
            List<String> topicKeyList = new ArrayList<>();
            for (int i = topicIdList.size() - 1; i >= 0; i--) {
                String topicId = topicIdList.get(i);
                String key1 = Constant.TOPIC_AUCTION + topicId;
                topicKeyList.add(key1);
            }
            List list = redisTemplate.opsForValue().multiGet(topicKeyList);

            if (list == null || list.size() == 0) {
                logger.error("topicAuctionScheduledTask：拍卖商品列表为空");
                continue;
            }
            for (Object topicObject : list) {
                Topic targetTopic = (Topic) topicObject;
                handleAuctionTopic(targetTopic);
            }
//把拍卖主题缓存清除掉
            redisTemplate.delete(topicKeyList);
        }
    }

    public void handleAuctionTopic(Topic topic) {

        String topicId = topic.getId();
        String winnerOfferId = topic.getoId();
        Long size = redisTemplate.opsForSet().size(Constant.TOPIC_AUCTION_SET + topicId);
        if (size == null || size == 0) {
            logger.info("handleAuctionTopic:没有竞价者topicId:{}", topicId);
            return;
        }
        boolean haveMoreOffer = true;
        while (haveMoreOffer) {
            long count;
            if (size > QUERY_COUNT) {
                size = size - QUERY_COUNT;
                count = QUERY_COUNT;
            } else {
                count = size;
                haveMoreOffer = false;
            }
            List<String> offerUserIdList = redisTemplate.opsForSet().pop(Constant.TOPIC_AUCTION_SET + topicId, count);
            List<String> offerKeyList = new ArrayList<>();
            for (String offerUserId : offerUserIdList) {
                offerKeyList.add(Constant.TOPIC_OFFER + topicId + ":" + offerUserId);
            }
            List offerObjectList = redisTemplate.opsForValue().multiGet(offerKeyList);

            Date updated = new Date();

            for (Object offerObject : offerObjectList) {
                Orders order = (Orders) offerObject;
                String uId = order.getuId();
                //需要在notifyOfferSuccess（）方法调用之前设置
                order.setUpdated(updated);
                if (order.getId().equals(winnerOfferId)) {
                    order.setStatus(Constant.ORDER_STATUS_OFFER_SUCCESS);
                    Date payExpiryDate = DateUtils.getDate(2);//2天的支有效期
                    order.setExpiryDate(payExpiryDate);//设置支付过期日期为2天，2天内没支付，在定时任务中，扣除相关的保证金给卖家
                    ordersMapper.updateByPrimaryKeySelective(order);
                    Date delayPayExpiryDate = DateUtils.addHour(payExpiryDate, 1);//在支付过期时间的基础上加1小时，作为自动扣除保证金的时间点
                    String payExpiryKey = DateUtils.formatDate(delayPayExpiryDate, DateUtils.DATE_FORMAT_DATE_HOUR);
                    redisTemplate.opsForSet().add(Constant.ORDER_PAY_EXPIRY_SET + payExpiryKey, order.getId());
                    redisTemplate.opsForValue().set(Constant.ORDER_RECORD + order.getId(), order);
                    notifyOfferSuccess(order, topic);
                    offerUserIdList.remove(uId);//去掉竞价成功者用户id,后面会以offerUserIdList批量通知用户竞价失败
                } else {
                    BigDecimal deposit = order.getDeposit();//保证金以出价时候保存进入的保证金为准，不以topic 当前的保证金为准，因为topic的保证金可能会因为重新编辑而改变
                    //分别在redis,mysql 中减去用户的保证金
                    BigDecimal mysqlDeposit = userMapper.reduceDeposit(uId, deposit);
                    order.setStatus(Constant.ORDER_STATUS_OFFER_FAIL);
                    redisTemplate.opsForHash().put(Constant.USER_SESSION + uId, Constant.DEPOSIT, mysqlDeposit);
                    logger.info("handleAuctionTopic:保证金减少，uId:{},offerId:{},amount:{},deposit:{}", uId, order.getId(), deposit, mysqlDeposit);
                }
                ordersMapper.updateByPrimaryKeySelective(order);
            }
            //通知出价失败，客户端显示通知信息，同步个人信息，保证金已经变化
            if (offerUserIdList.size() != 0) {
                JSONObject info = new JSONObject();
                info.put("userIdList", offerUserIdList);
                info.put("topic", topic);
                redisTemplate.convertAndSend(Constant.NOTIFY_OFFER_TO_TOPIC_FAIL, info);
            }
            //把缓存中的出价记录删除掉
            redisTemplate.delete(offerKeyList);

        }
    }

    private void notifyOfferSuccess(Orders orders, Topic topic) {
        String uId = orders.getuId();
//        Orders auctionOrder = new Orders();
        // orders.setSellerId(topic.getuId());
        orders.setTopic(topic);
        JSONObject info = new JSONObject();
        info.put("uId", uId);
        info.put("order", orders);
        redisTemplate.convertAndSend(Constant.NOTIFY_OFFER_TO_TOPIC_SUCCESS, info);
    }
}
